// 9.5 额外的string操作
/**
 * 除了顺序容器共同的操作之外，string类型还提供了一些额外的操作。
 * 这些操作中的大部分要么是提供string类和C风格字符数组之间的相互转换，要么是增加了允许我们用下标代替迭代器的版本。
 */

#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::cin, std::cout, std::endl;

int main()
{
    // 9.5.1　构造string的其他方法
    const char *cp = "Hello World!!!"; // 以空字符结束的数组
    char noNull[] = {'H', 'i'};        // 不是以空字符结束
    string s1(cp);                     // 拷贝cp中的字符直到遇到空字符；s == "Hello World!!!"
    string s2(noNull, 2);              // 从noNull拷贝两个字符；s2 == "Hi"
    string s3(noNull);                 // 未定义，noNull不是以空字符结尾
    string s4(cp + 6, 5);              // 从cp[6]开始拷贝5个字符，s4 == "World"
    string s5(s1, 6, 5);               // 从s1[6]开始拷贝5个字符，s5 == "World"
    string s6(s1, 6);                  // 从s1[6]开始拷贝直至s1末尾，s6 == "World!!!"
    string s7(s1, 6, 20);              // 正确，只拷贝至s1末尾，s7 == "World!!!"
    string s8(s1, 16);                 // 抛出一个out_of_range异常
    // substr操作
    string str("hello world");
    string str1 = str.substr(0, 5);  // str1 == "hello"
    string str2 = str.substr(6);     // str2 == "world"
    string str3 = str.substr(6, 11); // str3 == "world"
    string str4 = str.substr(12);    // 抛出一个out_of_range异常

    // 9.5.2 改变string的其他方法
    string s = "";
    s.insert(s.size(), 5, '!'); // 在s末尾插入5个感叹号
    s.erase(s.size() - 5, 5);   // 从s删除最后5个字符
    const char *cp1 = "Stately, plump Buck";
    s.assign(cp1, 7);              // s == "Stately"
    s.insert(s.size(), cp1 + 7);   // s == "Stately, plump Buck";
    s.insert(0, s2, 0, s2.size()); // 在s[0]之前插入s2中s2[0]开始的s2.size()个字符
    // append和replace函数
    string s("C++ Primer"), s2 = s;
    s.insert(s.size(), " 4th Ed."); // s == "C++ Primer 4th Ed."
    s2.append(" 4th Ed.");          // 同上
    s2.replace(11, 3, "5th");       // 从位置11开始，删除3个字符并插入"5th"
    // 改变string的多种重载函数

    // 9.5.3 string搜索操作
    // string搜索函数返回string：：size_type值，该类型是一个unsigned类型。
    // 因此，用一个int或其他带符号类型来保存这些函数的返回值不是一个好主意（参见2.1.2节，第33页）。
    string name("AnnaBelle");
    auto pos1 = name.find("Anna"); // pos1 == 0
    string lowercase("annabelle");
    pos1 = lowercase.find("Anna"); // pos1 == npos
    string numbers("0123456789"), name("r2d2");
    auto pos = name.find_first_of(numbers); // 返回1，即name中第一个数字的下标
    string dept("03714p3");
    auto pos = dept.find_first_not_of(numbers); // 返回5，字符p的下标
    // 指定在哪里搜索
    string::size_type pos = 0;
    while ((pos = name.find_first_of(numbers, pos)) != string::npos)
    {
        cout << "found number at index: " << pos
             << " element is " << name[pos] << endl;
        ++pos; // 移动到下一个字符
        // while的循环条件将pos重置为从pos开始遇到的第一个数字的下标。
        // 只要find_first_of返回一个合法下标，我们就打印当前结果并递增pos。
    }
    // 逆向搜索
    string river("Mississippi");
    auto first_pos = river.find("is"); // 返回1，第一个is的位置
    auto last_pos = river.rfind("is"); // 返回4，最后一个is的位置

    // 9.5.4 compare函数
    // 类似strcmp，根据s是等于、大于还是小于参数指定的字符串，s.compare返回0、正数或负数。
    string sc1("1234567");
    string sc2("12345678");
    sc1.compare(sc2);             // 比较sc1与sc2
    sc1.compare(0, 3, sc2);       // 将s中从0处开始的3个字符与sc2进行比较
    sc1.compare(0, 3, sc2, 1, 3); // 将s中从0处开始的3个字符与sc2中从1开始的3个字符进行比较
    const char *cp2 = "1234";     // 以空字符结尾的字符数组
    sc1.compare(cp);              // 比较s与cp指向的以空字符结尾的字符数组

    // 9.5.5 数值转换
    int i = 42;
    string s = std::to_string(i); // 将整数i转换为字符表示形式
    double d = std::stod(s);           // 将字符串s转换为浮点数
    string s2 = "pi = 3.14";
    d = std::stod(s2.substr(s2.find_first_of("+-.0123456789"))); // d == 3.14
    // 如果string不能转换为一个数值，这些函数抛出一个invalid_argument异常（参见5.6节，第173页）。
    // 如果转换得到的数值无法用任何类型来表示，则抛出一个out_of_range异常。
    return 0;
}